﻿using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using BoatNGoat.Input;
using SharpDX;
using SharpDX.Toolkit;
using SharpDX.Toolkit.Graphics;
using SharpDX.DirectInput;

namespace BoatNGoat
{   
    /// <summary>
    /// Simple SpriteBatchAndFont application using SharpDX.Toolkit.
    /// The purpose of this application is to use SpriteBatch and SpriteFont.
    /// </summary>
    public class BoatNGoatGame : Game
    {
        private readonly Stopwatch _fpsClock;
        private readonly GraphicsDeviceManager _graphicsDeviceManager;
        private RenderingEngine _renderingEngine;
        private SpriteBatch _spriteBatch;
        private SpriteFont _arial16BmFont;
        private int _frameCount;
        private string _fpsText;
        private SpriteSheet _spriteSheet;
        private SpriteSheet _playerSpriteSheet;
        private Player _player;
        private FollowTargetCameraController _playerFollower;
        private FollowTargetCameraController _goatFollower;

        private InputManager _inputManager;

        // I don't know if the camera controller should be here
        // It's both logic and rendering. It's a viewmodel or something.
        private ICameraController _view;
        private Stopwatch _goatCamClock;

        private Sprite _goat;

        // Damn static bool debouncer.
        private static bool _gKeyDebounce = false;
        private Keyboard _keyboard;

        private Vector2 _goatPos;
        private readonly LocalSimulation _simulation;
        private float GoatSpeed = 1;

        /// <summary>
        /// Initializes a new instance of the <see cref="BoatNGoatGame" /> class.
        /// </summary>
        /// <param name="simulation"></param>
        public BoatNGoatGame(LocalSimulation simulation)
        {
            _simulation = simulation;
            
            // Creates a graphics manager. This is mandatory.
            _graphicsDeviceManager = new GraphicsDeviceManager(this);

            // Force no vsync and use real timestep to print actual FPS
            _graphicsDeviceManager.SynchronizeWithVerticalRetrace = true;
            IsFixedTimeStep = false;

            // Setup the relative directory to the executable directory
            // for loading contents with the ContentManager
            Content.RootDirectory = "Content";

            _fpsClock = new Stopwatch();            
            _fpsText = string.Empty;            
        }


        protected override void LoadContent()
        {
            SpriteSheet mapTiles = new SpriteSheet(1, 4, 1,Content.Load<Texture2D>("Tiles.png"));
            _renderingEngine.SetTileSet(mapTiles);

            //_spriteSheet = new SpriteSheet(1, 10, Content.Load<Texture2D>("balls.png"));
            _spriteSheet = new SpriteSheet(2, 8, 1,Content.Load<Texture2D>("Gruff_Goat.png"));
            _goat = new Sprite(_spriteSheet);

            SpriteSheet playerSpriteSheet = new SpriteSheet(3, 4, 1, Content.Load<Texture2D>("PlayerSpriteSheet.png"));
            Sprite playerSprite = new Sprite(playerSpriteSheet);

            SpriteAnimation walkUpAnimation = new SpriteAnimation();
            walkUpAnimation.AddFrame(0, 0);
            walkUpAnimation.AddFrame(0, 1);
            walkUpAnimation.AddFrame(0, 0);
            walkUpAnimation.AddFrame(0, 2);
            playerSprite.CreateAnimation("walkup", walkUpAnimation);
            SpriteAnimation walkLeftAnimation = new SpriteAnimation();
            walkLeftAnimation.AddFrame(1, 0);
            walkLeftAnimation.AddFrame(1, 1);
            playerSprite.CreateAnimation("walkleft", walkLeftAnimation);
            SpriteAnimation walkDownAnimation = new SpriteAnimation();
            walkDownAnimation.AddFrame(2, 0);
            walkDownAnimation.AddFrame(2, 1);
            walkDownAnimation.AddFrame(2, 0);
            walkDownAnimation.AddFrame(2, 2);
            playerSprite.CreateAnimation("walkdown", walkDownAnimation);
            SpriteAnimation walkRightAnimation = new SpriteAnimation();
            walkRightAnimation.AddFrame(3, 0);
            walkRightAnimation.AddFrame(3, 1);
            playerSprite.CreateAnimation("walkright", walkRightAnimation);

            _renderingEngine.SetSpriteForEntityType(0, playerSprite);

            SpriteAnimation goatWalkLeftAnimation = new SpriteAnimation();
            goatWalkLeftAnimation.AddFrame(3, 0);
            goatWalkLeftAnimation.AddFrame(3, 1);
            _goat.CreateAnimation("walkleft", goatWalkLeftAnimation);
            SpriteAnimation goatWalkRightAnimation = new SpriteAnimation();
            goatWalkRightAnimation.AddFrame(5, 0);
            goatWalkRightAnimation.AddFrame(5, 1);
            _goat.CreateAnimation("walkright", goatWalkRightAnimation);
            SpriteAnimation goatWalkUpAnimation = new SpriteAnimation();
            goatWalkUpAnimation.AddFrame(4, 0);
            goatWalkUpAnimation.AddFrame(4, 1);
            _goat.CreateAnimation("walkup", goatWalkUpAnimation);
            SpriteAnimation goatWalkDownAnimation = new SpriteAnimation();
            goatWalkDownAnimation.AddFrame(0, 0);
            goatWalkDownAnimation.AddFrame(0, 1);
            _goat.CreateAnimation("walkdown", goatWalkDownAnimation);

            //_goat.SetAnimation("walkleft");
            _renderingEngine.SetSpriteForEntityType(1, _goat);
            int sceneCentreX = GraphicsDevice.BackBuffer.Width / 2;
            int sceneCentreY = GraphicsDevice.BackBuffer.Height / 2;
            _goatPos = new Vector2(sceneCentreX, sceneCentreY);

            // SpriteFont supports the following font file format:
            // - DirectX Toolkit MakeSpriteFont or SharpDX Toolkit tkfont
            // - BMFont from Angelcode http://www.angelcode.com/products/bmfont/
            _arial16BmFont = Content.Load<SpriteFont>("Arial16.tkfnt");

            _spriteBatch = new SpriteBatch(GraphicsDevice);


            
            base.LoadContent();
        }

        protected override void BeginRun()
        {
            _fpsClock.Start();
            _goatCamClock.Start();

            base.BeginRun();
        }

        protected override void Initialize()
        {
            Window.Title = "Boat 'n' Goat Pre-alpha";
            Window.AllowUserResizing = true;
            
            var input = new DirectInput();
            _inputManager = new InputManager(input, _simulation);

            _renderingEngine = new RenderingEngine(GraphicsDevice, _simulation);

            _view = new WhoooshCamera();
            _view.Camera = _renderingEngine.Camera;
            _goatCamClock = new Stopwatch();

            // Make dat player, eh?
            _player = new Player();
            _simulation.Player = _player;
            
            _renderingEngine.Camera.ViewCentre = new Vector2(250, 250);
            _playerFollower = new FollowTargetCameraController { Target = _player, Camera = _renderingEngine.Camera };
            _goatFollower = new FollowTargetCameraController { Target = null, Camera = _renderingEngine.Camera };
            _view = _playerFollower;

            System.Action followPlayer = (() => _view = _playerFollower);
            System.Action followNextGoat = (() =>
                {
                    var goats = _simulation.GetGoats();
                    int goatCount = goats.Count();
                    int k = Logic.Rnd.Next(0, goatCount - 1);
                    Server.IMobile randomGoat = goats.Skip(k).First();
                    _goatFollower.Target = randomGoat;
                    _view = _goatFollower;
                });
            InputManager.RegisterImpulseAction("CameraFollowLocalPlayer", followPlayer);
            InputManager.RegisterImpulseAction("CameraFollowNextGoat", followNextGoat);

            // This will call LoadContent() at some point.
            base.Initialize();
        }

        protected override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            // IsActive tells us the window has focus.
            // NB I read that this is always set to true on startup... if the user loads the app and then minimizes, we think we have focus when we don't!
            // In this case we can use GetForegroundWindow or something similar apparently (not sure where that is).
            if (IsActive)
            {
                _inputManager.HandleInput();
                //_player.Update(gameTime);

                //if (state.IsPressed(Key.P))
                //{
                //    _view = _playerFollower;
                //}
                //// Ugly bastard debouncing code... but press G to go to the next goat.
                //if (state.IsPressed(Key.G))
                //{
                //    if (!_gKeyDebounce)
                //    {
                //        //Goat Watch Mode
                //        //TODO: Put this somewhere sensible and/or delete
                //        var goats = _simulation.GetGoats();
                //        int goatCount = goats.Count();
                //        int k = Logic.Rnd.Next(0, goatCount - 1);
                //        Server.IMobile randomGoat = goats.Skip(k).First();
                //        _goatFollower.Target = randomGoat;

                //        _view = _goatFollower;
                //        _gKeyDebounce = true;
                //    }
                //}
                //else
                //{
                //    _gKeyDebounce = false;
                //}
            }

            _player.Update(gameTime);
            _simulation.Update(gameTime);
            _view.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            // Clear the screen and something about cornflakes.
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // Magic.
            _renderingEngine.Draw();

            // Update the FPS text
            _frameCount++;
            if (_fpsClock.ElapsedMilliseconds > 1000.0f)
            {
                _fpsText = string.Format("{0:F2} FPS", (float)_frameCount * 1000 / _fpsClock.ElapsedMilliseconds);
                _frameCount = 0;
                _fpsClock.Restart();
            }

            // Render the FPS counter text
            _spriteBatch.Begin();
            _spriteBatch.DrawString(_arial16BmFont, _fpsText, new Vector2(0, 32), Color.White);
            _spriteBatch.End();

            // Call Draw on any components.
            base.Draw(gameTime);
        }
    }
}
